Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit 61939fe28eb7721b773849ddaf25701cd1dbb3c0


Parents : b89dc9e
Author : Ivan <e46112d44649266d71fe2193e00a4710>
Signature : T66BB85Valid, signed by author
Date : 2026-07-27T08:13:22-05:00

feat: more rnode flasher and telephony fixes

Changes
Diff

diff --git a/android/app/src/main/java/com/meshchatx/rnode/RNodeFlasherActivity.java b/android/app/src/main/java/com/meshchatx/rnode/RNodeFlasherActivity.java
index 0701cc12..fceefd0f 100644
--- a/android/app/src/main/java/com/meshchatx/rnode/RNodeFlasherActivity.java
+++ b/android/app/src/main/java/com/meshchatx/rnode/RNodeFlasherActivity.java
@@ -27,6 +27,7 @@ import androidx.core.content.ContextCompat;
import com.meshchatx.AppSettingsLauncher;
import com.meshchatx.LocalhostTrustOkHttpClient;
import com.meshchatx.R;
+import com.meshchatx.RemoteBackendUrl;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -50,9 +51,10 @@ import okhttp3.ResponseBody;
*/
public final class RNodeFlasherActivity extends AppCompatActivity implements UsbSerialHub.Listener {
private static final int REQ_BT = 4401;
- private static final String LOCAL_API = "https://127.0.0.1:8000";
private static final String PREFS = "rnode_flasher";
private static final String PREF_BT_PROMPTED = "bt_perm_prompted";
+ private static final String SHELL_PREFS = "meshchatx_shell";
+ private static final String PREF_REMOTE_BACKEND_URL = "remote_backend_url";
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private final ExecutorService io = Executors.newSingleThreadExecutor();
@@ -429,11 +431,34 @@ public final class RNodeFlasherActivity extends AppCompatActivity implements Usb
});
}
+ /**
+ * Resolve the effective backend origin, matching MainActivity so the flasher
+ * still works when the shell is configured against a remote backend instead
+ * of the on-device local API.
+ */
+ private String resolveApiBaseUrl() {
+ SharedPreferences prefs = getSharedPreferences(SHELL_PREFS, MODE_PRIVATE);
+ return RemoteBackendUrl.resolveEffectiveUrl(prefs.getString(PREF_REMOTE_BACKEND_URL, null));
+ }
+
+ /**
+ * The localhost-only client skips certificate validation for the embedded
+ * self-signed 127.0.0.1 server. A configured remote backend must keep normal
+ * system certificate validation, so it gets a plain OkHttpClient instead.
+ */
+ private OkHttpClient httpClientFor(String apiBaseUrl) {
+ if (RemoteBackendUrl.isLocalBackendUrl(apiBaseUrl)) {
+ return LocalhostTrustOkHttpClient.get();
+ }
+ return new OkHttpClient.Builder().connectTimeout(15, java.util.concurrent.TimeUnit.SECONDS).build();
+ }
+
private byte[] fetchFirmwareZip(String filename) throws Exception {
- OkHttpClient client = LocalhostTrustOkHttpClient.get();
- // Resolve latest release asset URL via local API, then download through proxy.
- String releaseUrl = LOCAL_API + "/api/v1/tools/rnode/latest_release";
- Request releaseReq = authorizedGet(releaseUrl);
+ String apiBaseUrl = resolveApiBaseUrl();
+ OkHttpClient client = httpClientFor(apiBaseUrl);
+ // Resolve latest release asset URL via the backend API, then download through proxy.
+ String releaseUrl = apiBaseUrl + "/api/v1/tools/rnode/latest_release";
+ Request releaseReq = authorizedGet(apiBaseUrl, releaseUrl);
String assetUrl;
try (Response response = client.newCall(releaseReq).execute()) {
if (!response.isSuccessful() || response.body() == null) {
@@ -458,10 +483,10 @@ public final class RNodeFlasherActivity extends AppCompatActivity implements Usb
}
}
String downloadUrl =
- LOCAL_API
+ apiBaseUrl
+ "/api/v1/tools/rnode/download_firmware?url="
+ Uri.encode(assetUrl);
- Request dlReq = authorizedGet(downloadUrl);
+ Request dlReq = authorizedGet(apiBaseUrl, downloadUrl);
try (Response response = client.newCall(dlReq).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IllegalStateException("download_firmware HTTP " + response.code());
@@ -471,9 +496,9 @@ public final class RNodeFlasherActivity extends AppCompatActivity implements Usb
}
}
- private Request authorizedGet(String url) {
+ private Request authorizedGet(String apiBaseUrl, String url) {
Request.Builder builder = new Request.Builder().url(url).get();
- String cookie = CookieManager.getInstance().getCookie(LOCAL_API);
+ String cookie = CookieManager.getInstance().getCookie(apiBaseUrl);
if (cookie != null && !cookie.isEmpty()) {
builder.header("Cookie", cookie);
}

diff --git a/meshchatx.rsm b/meshchatx.rsm
index 4a08e144..b3e8cca1 100644
Binary files a/meshchatx.rsm and b/meshchatx.rsm differ

diff --git a/meshchatx/src/frontend/components/call/CallPage.vue b/meshchatx/src/frontend/components/call/CallPage.vue
index 868f0d8d..e948607c 100644
--- a/meshchatx/src/frontend/components/call/CallPage.vue
+++ b/meshchatx/src/frontend/components/call/CallPage.vue
@@ -695,14 +695,18 @@
id="web-audio-toggle"
:model-value="webAudioBridgeEnabled"
:disabled="webAudioBridgeRequired"
- label="Web Audio Bridge"
+ :label="
+ isMeshChatXAndroid()
+ ? 'Native Audio Bridge'
+ : 'Web Audio Bridge'
+ "
@update:model-value="onToggleWebAudio"
/>
<div class="text-xs text-gray-500 dark:text-zinc-400 px-1">
<template v-if="isMeshChatXAndroid()">
- Required on Android. Calls use the native mic and
- speaker attached through the audio bridge (not the
- browser getUserMedia path).
+ Always on for Android calls. Audio goes through the
+ phone's native mic and speaker (AudioRecord and
+ AudioTrack), never the WebView's browser microphone API.
</template>
<template v-else-if="webAudioBridgeRequired">
Required on this host (no LXST host audio device).
@@ -777,7 +781,7 @@
</div>
<!-- Web Audio Device Selection -->
- <div v-if="webAudioBridgeEnabled" class="flex flex-col gap-2 mt-2">
+ <div v-if="showWebAudioDeviceSelector" class="flex flex-col gap-2 mt-2">
<div class="flex flex-col gap-1">
<div
class="text-[10px] font-bold text-gray-500 uppercase tracking-widest px-1"
@@ -1892,6 +1896,12 @@ export default {
webAudioBridgeEnabled() {
return Boolean(this.webAudioBridgeRequired || this.config?.telephone_web_audio_enabled);
},
+ showWebAudioDeviceSelector() {
+ // Android routes call audio through the native mic/speaker, not a
+ // browser device picker, so the Web Audio device selector only
+ // applies to browser/Electron hosts.
+ return this.webAudioBridgeEnabled && !this.isMeshChatXAndroid();
+ },
isMicMuted() {
return this.localMicMuted;
},

diff --git a/meshchatx/src/frontend/components/tools/RNodeFlasherPage.vue b/meshchatx/src/frontend/components/tools/RNodeFlasherPage.vue
index df5e3f28..dbbe33d2 100644
--- a/meshchatx/src/frontend/components/tools/RNodeFlasherPage.vue
+++ b/meshchatx/src/frontend/components/tools/RNodeFlasherPage.vue
@@ -507,8 +507,20 @@ export default {
throw e;
}
},
+ _requireWebSerialOrReopenNative() {
+ if (typeof navigator !== "undefined" && navigator.serial) {
+ return;
+ }
+ if (this.androidBridge.hasNativeRNodeFlasher()) {
+ this.androidBridge.openRNodeFlasher();
+ const err = new Error(this.$t("tools.rnode_flasher.errors.use_native_flasher"));
+ err.code = "ANDROID_NATIVE_FLASHER_REQUIRED";
+ throw err;
+ }
+ },
async _openTransport() {
if (this.connectionMethod === TRANSPORT_SERIAL) {
+ this._requireWebSerialOrReopenNative();
const transport = await SerialTransport.request();
await transport.open({ baudRate: 115200 });
this.connectedTransportLabel = transport.description();
@@ -666,6 +678,7 @@ export default {
this.isEnteringDfuMode = true;
this.flashError = null;
try {
+ this._requireWebSerialOrReopenNative();
const transport = await SerialTransport.request();
const flasher = new Nrf52DfuFlasher(transport.port);
await flasher.enterDfuMode();
@@ -758,6 +771,7 @@ export default {
this.flashingStatus = this.$t("tools.rnode_flasher.connecting_device");
let transport = null;
try {
+ this._requireWebSerialOrReopenNative();
transport = await SerialTransport.request();
const flasher = new Nrf52DfuFlasher(transport.port);
await flasher.flash(this.firmwareFile, (percentage, message) => {
@@ -791,6 +805,7 @@ export default {
this.flashingStatus = this.$t("tools.rnode_flasher.connecting_device");
let transport = null;
try {
+ this._requireWebSerialOrReopenNative();
transport = await SerialTransport.request();
const blobReader = new window.zip.BlobReader(this.firmwareFile);

diff --git a/meshchatx/src/frontend/locales/de.json b/meshchatx/src/frontend/locales/de.json
index 346d9a59..a9efb7ec 100644
--- a/meshchatx/src/frontend/locales/de.json
+++ b/meshchatx/src/frontend/locales/de.json
@@ -2529,7 +2529,8 @@
"invalid_host": "Bitte geben Sie einen gültigen Host oder eine gültige IP-Adresse ein.",
"bluetooth_flash_unsupported": "Bluetooth kann nicht zum Flashen von Firmware verwendet werden. Verwenden Sie seriell oder WLAN zum Flashen und kehren Sie dann für die Verwaltung zu Bluetooth zurück.",
"failed_diagnostics": "Fehler beim Ausführen der Diagnose: {error}",
- "failed_detect_with_reason": "Fehler beim Erkennen von RNode: {error}"
+ "failed_detect_with_reason": "Fehler beim Erkennen von RNode: {error}",
+ "use_native_flasher": "Der USB-Zugriff unter Android erfolgt über das native RNode-Flasher-Fenster. Es wird jetzt erneut geöffnet."
},
"alerts": {
"firmware_downloaded": "Empfohlene Firmware wurde heruntergeladen und geladen.",

diff --git a/meshchatx/src/frontend/locales/en.json b/meshchatx/src/frontend/locales/en.json
index 216b397e..1783ac3d 100644
--- a/meshchatx/src/frontend/locales/en.json
+++ b/meshchatx/src/frontend/locales/en.json
@@ -2739,7 +2739,8 @@
"invalid_host": "Please enter a valid host or IP address.",
"bluetooth_flash_unsupported": "Bluetooth cannot be used to flash firmware. Use Serial or WiFi to flash, then return to Bluetooth for management.",
"failed_diagnostics": "Failed to run diagnostics: {error}",
- "failed_detect_with_reason": "Failed to detect RNode: {error}"
+ "failed_detect_with_reason": "Failed to detect RNode: {error}",
+ "use_native_flasher": "USB access on Android goes through the native RNode flasher window. Reopening it now."
},
"alerts": {
"firmware_downloaded": "Recommended firmware has been downloaded and loaded.",

diff --git a/meshchatx/src/frontend/locales/es.json b/meshchatx/src/frontend/locales/es.json
index 29d6adca..f14f1f26 100644
--- a/meshchatx/src/frontend/locales/es.json
+++ b/meshchatx/src/frontend/locales/es.json
@@ -2735,7 +2735,8 @@
"invalid_host": "Ingrese un host o dirección IP válida.",
"bluetooth_flash_unsupported": "Bluetooth no se puede usar para flashear firmware. Use Serial o WiFi para flashear, luego vuelva a Bluetooth para la gestión.",
"failed_diagnostics": "Error al ejecutar el diagnóstico: {error}",
- "failed_detect_with_reason": "Error al detectar RNode: {error}"
+ "failed_detect_with_reason": "Error al detectar RNode: {error}",
+ "use_native_flasher": "El acceso USB en Android pasa por la ventana nativa del flasher de RNode. Reabriéndola ahora."
},
"alerts": {
"firmware_downloaded": "El firmware recomendado ha sido descargado y cargado.",

diff --git a/meshchatx/src/frontend/locales/fi.json b/meshchatx/src/frontend/locales/fi.json
index 4ead2661..48a9410a 100644
--- a/meshchatx/src/frontend/locales/fi.json
+++ b/meshchatx/src/frontend/locales/fi.json
@@ -2735,7 +2735,8 @@
"invalid_host": "Anna kelvollinen isäntä- tai IP-osoite.",
"bluetooth_flash_unsupported": "Bluetoothia ei voi käyttää laiteohjelmiston päivittämiseen. Käytä Serial- tai WiFi-yhteyttä päivittämiseen, palaa sitten Bluetoothiin hallintaa varten.",
"failed_diagnostics": "Diagnostiikan suorittaminen epäonnistui: {error}",
- "failed_detect_with_reason": "RNodea ei voitu tunnistaa: {error}"
+ "failed_detect_with_reason": "RNodea ei voitu tunnistaa: {error}",
+ "use_native_flasher": "USB-käyttö Androidissa tapahtuu natiivin RNode-flasher-ikkunan kautta. Avataan se nyt uudelleen."
},
"alerts": {
"firmware_downloaded": "Suositeltu laiteohjelmisto on ladattu ja asennettu.",

diff --git a/meshchatx/src/frontend/locales/fr.json b/meshchatx/src/frontend/locales/fr.json
index aef6369b..497fb737 100644
--- a/meshchatx/src/frontend/locales/fr.json
+++ b/meshchatx/src/frontend/locales/fr.json
@@ -2735,7 +2735,8 @@
"invalid_host": "Veuillez entrer un hôte ou une adresse IP valide.",
"bluetooth_flash_unsupported": "Le Bluetooth ne peut pas être utilisé pour flasher le firmware. Utilisez Serial ou WiFi pour flasher, puis revenez au Bluetooth pour la gestion.",
"failed_diagnostics": "Échec de l'exécution des diagnostics : {error}",
- "failed_detect_with_reason": "Échec de la détection du RNode : {error}"
+ "failed_detect_with_reason": "Échec de la détection du RNode : {error}",
+ "use_native_flasher": "L'accès USB sur Android passe par la fenêtre native du flasher RNode. Réouverture en cours."
},
"alerts": {
"firmware_downloaded": "Le firmware recommandé a été téléchargé et chargé.",

diff --git a/meshchatx/src/frontend/locales/it.json b/meshchatx/src/frontend/locales/it.json
index 8add349a..adb5a385 100644
--- a/meshchatx/src/frontend/locales/it.json
+++ b/meshchatx/src/frontend/locales/it.json
@@ -2787,7 +2787,8 @@
"invalid_host": "Inserisci un host o indirizzo IP valido.",
"bluetooth_flash_unsupported": "Bluetooth non può essere utilizzato per scrivere il firmware. Usa Serial o WiFi per scrivere, poi torna a Bluetooth per la gestione.",
"failed_diagnostics": "Esecuzione diagnostica fallita: {error}",
- "failed_detect_with_reason": "Rilevamento RNode fallito: {error}"
+ "failed_detect_with_reason": "Rilevamento RNode fallito: {error}",
+ "use_native_flasher": "L'accesso USB su Android avviene tramite la finestra nativa del flasher RNode. Riapertura in corso."
},
"alerts": {
"firmware_downloaded": "Il firmware consigliato è stato scaricato e caricato.",

diff --git a/meshchatx/src/frontend/locales/nl.json b/meshchatx/src/frontend/locales/nl.json
index 43a176a9..0a660b57 100644
--- a/meshchatx/src/frontend/locales/nl.json
+++ b/meshchatx/src/frontend/locales/nl.json
@@ -2735,7 +2735,8 @@
"invalid_host": "Voer een geldige host of IP-adres in.",
"bluetooth_flash_unsupported": "Bluetooth kan niet worden gebruikt om firmware te flashen. Gebruik Seriële of WiFi om te flashen, keer daarna terug naar Bluetooth voor beheer.",
"failed_diagnostics": "Diagnostiek uitvoeren mislukt: {error}",
- "failed_detect_with_reason": "RNode detectie mislukt: {error}"
+ "failed_detect_with_reason": "RNode detectie mislukt: {error}",
+ "use_native_flasher": "USB-toegang op Android verloopt via het native RNode-flasher-venster. Wordt nu opnieuw geopend."
},
"alerts": {
"firmware_downloaded": "Aanbevolen firmware is gedownload en geladen.",

diff --git a/meshchatx/src/frontend/locales/ru.json b/meshchatx/src/frontend/locales/ru.json
index dd489456..5f6ec658 100644
--- a/meshchatx/src/frontend/locales/ru.json
+++ b/meshchatx/src/frontend/locales/ru.json
@@ -2529,7 +2529,8 @@
"invalid_host": "Пожалуйста, введите действительный хост или IP-адрес.",
"bluetooth_flash_unsupported": "Bluetooth нельзя использовать для прошивки. Используйте последовательный порт или WiFi для прошивки, затем вернитесь к Bluetooth для управления.",
"failed_diagnostics": "Не удалось выполнить диагностику: {error}",
- "failed_detect_with_reason": "Не удалось обнаружить RNode: {error}"
+ "failed_detect_with_reason": "Не удалось обнаружить RNode: {error}",
+ "use_native_flasher": "Доступ к USB на Android выполняется через окно нативной программы прошивки RNode. Открываем его снова."
},
"alerts": {
"firmware_downloaded": "Рекомендуемая прошивка была скачана и загружена.",

diff --git a/meshchatx/src/frontend/locales/zh.json b/meshchatx/src/frontend/locales/zh.json
index 015d9ec5..75e0f3f8 100644
--- a/meshchatx/src/frontend/locales/zh.json
+++ b/meshchatx/src/frontend/locales/zh.json
@@ -2735,7 +2735,8 @@
"invalid_host": "请输入有效的主机名或 IP 地址。",
"bluetooth_flash_unsupported": "蓝牙无法用于刷写固件。请使用串口或 WiFi 进行刷写,然后返回蓝牙进行管理。",
"failed_diagnostics": "运行诊断失败:{error}",
- "failed_detect_with_reason": "检测 RNode 失败:{error}"
+ "failed_detect_with_reason": "检测 RNode 失败:{error}",
+ "use_native_flasher": "Android 上的 USB 访问需要通过原生 RNode 刷机器窗口进行,正在重新打开。"
},
"alerts": {
"firmware_downloaded": "推荐的固件已下载并装入.",


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────